package screen2;

import procedure.Find;
import procedure.Node;
import procedure.TestProcess;

import java.util.*;

/**
 * @author sk.z
 */
public class Screen {

    private List<Point> pointList = new ArrayList<>();
    private Point[][] matrix;

    public Screen(Node process) {
        init(process);
    }

    public static void main(String[] args) {
        Screen screen = new Screen(TestProcess.B());
        screen.print();
    }

    public void print() {
        calculateX();   //计算x坐标
        makeMatrix();   //建立矩阵
        makeLine();
        showMatrix();
    }

    private void calculateX() {
        for (Point p : pointList) {
            p.x = p.sumX * 2 / p.appear;
        }
        //判断是否存在奇数
        for (Point p : pointList) {
            if (!isEven(p.x)) {
                return;
            }
        }
        //都是偶数 全部除2
        pointList.forEach(e -> e.x = e.x / 2);
    }

    private void makeMatrix() {
        int high = 0;
        int length = 0;
        for (Point p : pointList) {
            high = Math.max(high, p.x);
            length = Math.max(length, p.y);
        }

        matrix = new Point[high + 1][length + 1];
        for (Point p : pointList) {
            matrix[p.x][p.y] = p;
        }

        for (int x = 0; x < matrix.length; x++) {
            for (int y = 0; y < matrix[x].length; y++) {
                if (Objects.isNull(matrix[x][y])) {
                    Point p = new Point(0);
                    p.x = x;
                    p.y = y;
                    matrix[x][y] = p;
                }
            }
        }
    }

    private void makeLine() {
        for (Point curPoint : pointList) {
            int nextType = 0;    //0-默认 1-分流 2-合流
            if (curPoint.afterList.size() == 0) {
                continue;
            } else if (curPoint.afterList.size() == 1) {
                Point next = curPoint.afterList.get(0);
                if (next.preList.size() > 1) {
                    nextType = 2;
                }
            } else {
                nextType = 1;
            }

            for (Point next : curPoint.afterList) {
                autoWired(curPoint, next, nextType);
            }
        }
    }

    private void autoWired(Point from, Point to, int nextType) {
        int fromX = from.x;
        int fromY = from.y;
        int toX = to.x;
        int toY = to.y;

        if (fromX == toX) {
            drawLine(from, to);
            return;
        }

        int kneeX;
        int kneeY;
        if (nextType == 1) {
            //分流
            kneeX = toX;
            kneeY = fromY;
        } else {
            //合流
            kneeX = fromX;
            kneeY = toY;
        }

        Point knee = matrix[kneeX][kneeY];
        drawLine(from, knee);
        drawLine(knee, to);

        if (fromX > toX) {
            knee.name = "/";
        } else {
            knee.name = "\\";
        }
        knee.type = 1;
    }

    private void drawLine(Point from, Point to) {
        int start;
        int end;
        if (from.x == to.x) {
            start = Math.min(from.y, to.y);
            end = Math.max(from.y, to.y);
            start++;
            while (start < end) {
                Point point = matrix[from.x][start];
                if (point.type == 0) {
                    point.name = "-";
                    point.type = 1;
                }
                start++;
            }
        } else if (from.y == to.y) {
            start = Math.min(from.x, to.x);
            end = Math.max(from.x, to.x);
            start++;
            while (start < end) {
                Point point = matrix[start][from.y];
                if (point.type == 0) {
                    point.name = "|";
                    point.type = 1;
                }
                start++;
            }
        }
    }


    private void showMatrix() {
        for (int x = 0; x < matrix.length; x++) {
            for (int y = 0; y < matrix[x].length; y++) {
                Point p = matrix[x][y];
                if (p.type == 0) {
                    System.out.print("    ");
                } else if (p.type == 1) {
                    System.out.print(" " + p.name + "  ");
                } else {
                    if (p.name.length()==1){
                        System.out.print("[" + p.name+ " ]");
                    }else if (p.name.length()>2) {
                        System.out.print("[" + p.name.substring(0, 2) + "]");
                    }else {
                        System.out.print("[" + p.name+ "]");
                    }
                }
            }
            System.out.println();
        }
    }

    private void init(Node process) {
        List<List<Node>> splitList = Find.travelGetAllSingleLine(process);

        //Node -> Point
        Map<Node, Point> nodeMapPoint = new HashMap<>();
        Map<Point, Node> pointMapNode = new HashMap<>();
        for (int x = 0; x < splitList.size(); x++) {
            List<Node> line = splitList.get(x);
            for (int y = 0; y < line.size(); y++) {
                Node n = line.get(y);
                if (nodeMapPoint.containsKey(n)) {
                    Point p = nodeMapPoint.get(n);
                    p.appear++;
                    p.sumX = p.sumX + x;
                    p.y = Math.max(p.y, y); //设定y坐标
                    continue;
                }

                Point p = new Point(2);
                p.name = n.getName();
                p.appear++;
                p.sumX = x;
                p.y = y;    //设定y坐标

                nodeMapPoint.put(n, p);
                pointMapNode.put(p, n);
                pointList.add(p);
            }
        }

        //connect
        for (Point p : pointList) {
            Node node = pointMapNode.get(p);
            if (node.getPreList().size() > 0) {
                for (Node n : node.getPreList()) {
                    p.preList.add(nodeMapPoint.get(n));
                }
            }
            if (node.getAfterList().size() > 0) {
                for (Node n : node.getAfterList()) {
                    p.afterList.add(nodeMapPoint.get(n));
                }
            }
        }
    }

    private int midNum(int i) {
        if (i == 0) {
            return 0;
        }
        return i / 2;
    }

    private boolean isEven(int i) {
        return i % 2 == 0;
    }
}
